#ifndef __Matriz_H__
#define __Matriz_H__

#include <vector>
#include <fstream>
#include <assert.h>

using namespace std;

class Matriz{

	private:
        int _columnas;
        int _filas;
        vector<vector<double> > _m;

    public:

        Matriz(int filas, int columnas){
            _filas=filas;
            _columnas=columnas;
            _m.resize(filas);
            int i;
            for (i=0;i<filas;i++){
                _m[i].resize(columnas);
            }
        }

        Matriz(Matriz* m11,Matriz* m12,Matriz* m21,Matriz* m22){
            _filas= m11->_filas + m21->_filas ;
            _columnas= m11->_columnas + m12->_columnas;
            _m.resize(_filas);
            int i,j;
            for (i=0;i<_filas;i++){
                _m[i].resize(_columnas);
            }
            //matriz m11
            for (i=0;i<(m11->_filas);i++){
                for (j=0;j<(m11->_columnas);j++){
                    _m[i][j]=m11->_m[i][j];
                }
            }
            //matriz m12
            for (i=0;i<(m12->_filas);i++){
                for (j=0;j<(m12->_columnas);j++){
                    _m[i][j+m11->_columnas]=m12->_m[i][j];
                }
            }
            //matriz 21
            for (i=0;i<(m21->_filas);i++){
                for (j=0;j<(m21->_columnas);j++){
                    _m[i+m11->_filas][j]=m21->_m[i][j];
                }
            }
            //matriz m22
            for (i=0;i<(m22->_filas);i++){
                for (j=0;j<(m22->_columnas);j++){
                    _m[i+m11->_filas][j+m11->_columnas]=m22->_m[i][j];
                }
            }
        }

		Matriz(Matriz* m){
			_filas = m->cantFilas();
			_columnas = m->cantColumnas();
			_m.resize(_filas);
			int i,j;
            for (i=0;i<_filas;i++){
                _m[i].resize(_columnas);
            }

			for (i=0;i<_filas; i++){
				for (j=0;j<_columnas; j++){
					_m[i][j] = m->getValor(i+1,j+1);
				}
			}
		}

		void concatenar(Matriz* m){
			int i;
			for (i=0; i<_filas; i++){
				_m[i].resize(_columnas+1);
				_m[i][_columnas] = m->getValor(i+1,1);

			}
			_columnas = _columnas+1;
		}

        void llenarConIdentidad(){
			int i;

			assert(_filas == _columnas);

			for (i=0;i<_filas;i++){
				_m[i][i] = 1;
			}
		}

		void multiplicarEscalar(double valor){
			int i, j;

			for (i=0;i<_filas;i++){
				for (j=0;j<_columnas;j++){
					_m[i][j] *= valor;
				}
			}
		}

        void setValor(int fila, int columna, double valor){
            _m[fila-1][columna-1] = valor;
        }

        double getValor(int fila, int columna){
            return _m[fila-1][columna-1];
        }

        int cantFilas(){
            return _filas;
        }

        int cantColumnas(){
            return _columnas;
        }

        void suma(Matriz* m1){
            int i,j;

            for (i=0;i<_filas;i++){
                for (j=0;j<_columnas;j++){
                    //_m[i][j] += m1->getValor(i+1,j+1);
                    _m[i][j] += m1->_m[i][j];
                }
            }
        }

        void mostrar(){
			int i, j;
            cout.precision(10);
			for (i=0;i<_filas;i++){
				for (j=0;j<_columnas;j++){
					cout << scientific << _m[i][j]<<" ";
					//printf("%4.3f ",_m[i][j]);
				}
				cout<<"\n";
				//printf("\n");
			}
		}

		void mostrar(int precision, char notacion){
			int i, j;

            cout.precision(precision);
			for (i=0;i<_filas;i++){
				for (j=0;j<_columnas;j++){
					if (notacion == 'c')
						cout << scientific << _m[i][j]<<" ";
					else
						cout << fixed << _m[i][j]<<" ";
				}
				cout<<"\n";
			}
		}

		void intercambiarFilas(int i, int j){
			if (i!=j)
				_m[i-1].swap(_m[j-1]);
		}

		Matriz* resolverSistema(Matriz* b){
		    int i,j,k;
		    double coeficiente;
		    double sumatoria;

			//Pasa a ser una nueva matriz con la nueva columna b
			concatenar(b);

		    //Triangulacion//
		    for (k=0; k<_columnas-2; k++){

                for (i=k+1; i<_filas; i++){
                    coeficiente = _m[i][k]/_m[k][k];
                    _m[i][k] = 0;

                    for (j=k+1; j<_columnas; j++){
                        _m[i][j] = _m[i][j] - (coeficiente*_m[k][j]);
                    }
                }
		    }

		    //Sustitucion hacia atras//
		    Matriz* x = new Matriz(_filas,1);

		    x->_m[_filas-1][0] = _m[_filas-1][_columnas-1]/_m[_filas-1][_columnas-2];

		    for (i=_filas-2; i>=0; i--){
		        sumatoria = 0;
		        for (j = i+1; j<_columnas-1; j++){
		            sumatoria = sumatoria + (_m[i][j]*x->_m[j][0]);
		        }
		        x->_m[i][0] = (_m[i][_columnas-1]-sumatoria)/_m[i][i];
		    }
		    ////////////////////////////////////////////

		    return x;
		}

		vector<double> objetoPos(int i){

		    double x = 0;
            double y = 0;
            double z = 0;

            x = _m[(_filas/2)+3*(i-1)][0];
            y = _m[(_filas/2)+3*(i-1)+1][0];
            z = _m[(_filas/2)+3*(i-1)+2][0];

            vector<double> res;
            res.push_back(x);
            res.push_back(y);
            res.push_back(z);

            return res;

        }

		void multiplicar(Matriz* m2){

			int i, j, k;

			assert(cantColumnas() == m2->cantFilas());

			Matriz* c = new Matriz(cantFilas(), m2->cantColumnas());

			for (i = 1; i <= cantFilas(); i++) {
				for (j = 1; j <= m2->cantColumnas(); j++) {
					c->setValor(i,j,0);
					for (k = 1; k <=cantColumnas(); k++) {
						c->setValor(i,j,c->getValor(i,j)+(getValor(i,k) * m2->getValor(k,j)));
					}
				}
			}

			this->_m = c->_m;
			_filas = c->cantFilas();
			_columnas = c->cantColumnas();

			delete(c);
		}


};

#endif // __Matriz__
